package org.luo.lan.server.handlers;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.luo.lan.common.handler.ControlTypeConstant;
import org.luo.lan.common.handler.Request;
import org.luo.lan.common.util.StringUtils;
import org.luo.lan.server.constants.AttrConstants;
import org.luo.lan.server.factory.BridgeChannelFactory;
import org.luo.lan.server.factory.SessionFactory;

import java.io.IOException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @Auther: luobiao
 * @Date: 2020/9/19 09:15
 * @Description:
 */
@Slf4j
public abstract class AbstractServerHandler extends SimpleChannelInboundHandler<ByteBuf> {

    @Override
    public void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) {
        NioSocketChannel proxyChannel=(NioSocketChannel) ctx.channel();
        NioSocketChannel bridgeChannel=proxyChannel.attr(AttrConstants.BRIDGE_CHANNEL).get();

        if (bridgeChannel == null) {
            String proxyKey=getProxyKey(ctx,byteBuf);
            if (StringUtils.isNotBlank(proxyKey)) {
                bridgeChannel=bind(ctx,proxyKey);
                if (bridgeChannel == null) {
                    return;
                }
            }else{
                return;
            }
        }

        byte[] bytes=getBytes(byteBuf);
        log.debug("目标请求的channel={}，请求的内容为：",proxyChannel);
/*        log.debug("---------------------------------目标请求内容begin---------------------------------------");
        log.debug(new String(bytes));
        log.debug("-----------------------------------目标请求内容end-------------------------------------");*/

        String target=proxyChannel.attr(AttrConstants.TARGET).get();
        String sessionKey=proxyChannel.attr(AttrConstants.SEESION_KEY).get();
        bridgeChannel.writeAndFlush(Request.SUCCESS(ControlTypeConstant.TRANSPORT, bytes).addHeader(sessionKey + "@" + target));
    }

    protected NioSocketChannel bind(ChannelHandlerContext ctx,String proxyKey){

        NioSocketChannel bridgeChannel=BridgeChannelFactory.INSTANCE.getChannel(proxyKey);
        if (bridgeChannel == null) {
            log.error("客户端未启动或已关闭！remoteAddress={}",ctx.channel().remoteAddress());
            ctx.close();
            return null;
        }
        NioSocketChannel proxyChannel=(NioSocketChannel) ctx.channel();
        proxyChannel.attr(AttrConstants.BRIDGE_CHANNEL).set(bridgeChannel);
        proxyChannel.attr(AttrConstants.TARGET).set(BridgeChannelFactory.INSTANCE.getTarget(proxyKey));
        proxyChannel.attr(AttrConstants.IS_MANUAL_CLOSE).set(false);
        String sessionKey=SessionFactory.INSTANCE.createSession(proxyChannel);
        proxyChannel.attr(AttrConstants.SEESION_KEY).set(sessionKey);
        return bridgeChannel;
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx){
        NioSocketChannel proxyChannel=(NioSocketChannel) ctx.channel();
        String sessionKey=proxyChannel.attr(AttrConstants.SEESION_KEY).get();
        if (StringUtils.isNotBlank(sessionKey)) {
            SessionFactory.INSTANCE.removeSession(sessionKey);
            NioSocketChannel bridgeChannel= ctx.channel().attr(AttrConstants.BRIDGE_CHANNEL).get();
            Boolean isManualClose=proxyChannel.attr(AttrConstants.IS_MANUAL_CLOSE).get();
            //是客户端手动关闭就不需要给客户端发关闭请求了
            if (bridgeChannel != null && (!isManualClose)) {
                log.debug("发送会话关闭请求，sessionKey={}",sessionKey);
                bridgeChannel.writeAndFlush(Request.SUCCESS(ControlTypeConstant.CLOSE_SESSION, "服务器端主动关闭会话通道！").addHeader(sessionKey));
            }
            log.debug(sessionKey+"会话移除成功！");
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
        if (!(cause instanceof IOException)) {
            cause.printStackTrace();
        }
        log.info(cause.getMessage());
    }

    abstract String getProxyKey(ChannelHandlerContext ctx,ByteBuf bytes);
    abstract byte[] getBytes(ByteBuf byteBuf);
}
